use std::path::Path;

use bc_base::{
    PrivateKeyBytes, PublicKeyBytes, PublicKeyHashBytes, TransactionId, transaction::Transaction,
    wallet::wallet_cache::WalletCache, wallet::wallet_tx::WalletTx,
};
use bc_db::DBError;

#[cfg(debug_assertions)]
use bc_base::wallet::ItemNumberInWalletDb;

use crate::BlockDataStorageError;
use crate::inner_wallet_storage::{WalletDbKey, WalletStorageBase};

#[derive(Debug)]
pub struct WalletStorage {
    wallet_db: WalletStorageBase,
}

// simple warpper
impl WalletStorage {
    pub fn new(path: &Path) -> Result<Self, DBError> {
        Ok(Self {
            wallet_db: WalletStorageBase::new(path)?,
        })
    }

    pub async fn flush_database(&self) -> Result<(), BlockDataStorageError> {
        self.wallet_db.flush_database().await?;
        Ok(())
    }

    pub async fn load_to_wallet_cache(&self, wallet_cache: WalletCache) -> WalletCache {
        self.wallet_db.load_to_wallet_cache(wallet_cache).await
    }

    pub async fn store_wallet(
        &self,
        wallet_cache: &WalletCache,
    ) -> Result<(), BlockDataStorageError> {
        self.wallet_db.store_wallet(wallet_cache).await?;
        Ok(())
    }
}

// for account
impl WalletStorage {
    pub async fn put_keypair_to_wallet_db(
        &self,
        public_key: &PublicKeyBytes,
        private_key: &PrivateKeyBytes,
    ) -> Result<(), BlockDataStorageError> {
        let private_key = bincode::serialize(private_key).unwrap();
        self.wallet_db
            .put(WalletDbKey::AccountKeyPair(public_key.clone()), private_key)
            .await?;

        Ok(())
    }

    pub async fn del_account_from_wallet_db(
        &self,
        public_key: &PublicKeyBytes,
    ) -> Result<(), BlockDataStorageError> {
        self.wallet_db
            .erase(WalletDbKey::AccountKeyPair(public_key.clone()))
            .await?;
        let pub_key_hash = public_key.to_public_key_hash();
        self.wallet_db
            .erase(WalletDbKey::AccountName(pub_key_hash))
            .await?;

        let default_key = self.get_default_key_from_wallet_db().await.unwrap();
        if default_key == Some(pub_key_hash) {
            self.del_default_key_from_wallet_db().await?;
        }
        Ok(())
    }

    // pub async fn put_default_key_to_wallet_db(
    //     &self,
    //     public_key_hash: &PublicKeyHashBytes,
    // ) -> Result<(), BlockDataStorageError> {
    //     let value = bincode::serialize(public_key_hash).unwrap();
    //     self.wallet_db.put(WalletDbKey::DefaultKey, value).await?;
    //     Ok(())
    // }

    pub async fn del_default_key_from_wallet_db(&self) -> Result<(), BlockDataStorageError> {
        self.wallet_db.erase(WalletDbKey::DefaultKey).await?;
        Ok(())
    }

    pub async fn get_default_key_from_wallet_db(
        &self,
    ) -> Result<Option<PublicKeyHashBytes>, BlockDataStorageError> {
        let value = self.wallet_db.get(WalletDbKey::DefaultKey).await?;
        match value {
            Some(value) => {
                let value = bincode::deserialize::<PublicKeyHashBytes>(&value).unwrap();
                Ok(Some(value))
            }
            None => Ok(None),
        }
    }
}

// for tx
impl WalletStorage {
    // pub async fn get_wallet_tx_from_wallet_db(
    //     &self,
    //     tx_id: &TransactionId,
    // ) -> Result<Option<WalletTx>, BlockDataStorageError> {
    //     let value = self.wallet_db.get(WalletDbKey::WalletTx(*tx_id)).await?;
    //     match value {
    //         Some(value) => {
    //             let wtx = bincode::deserialize::<WalletTx>(&value).unwrap();
    //             Ok(Some(wtx))
    //         }
    //         None => Ok(None),
    //     }
    // }

    pub async fn put_tx_to_wallet_db(
        &self,
        tx_id: &TransactionId,
        tx: &Transaction,
    ) -> Result<(), BlockDataStorageError> {
        let wtx = WalletTx::new(tx);
        let value = bincode::serialize(&wtx).unwrap();
        self.wallet_db
            .put(WalletDbKey::WalletTx(*tx_id), value)
            .await?;
        Ok(())
    }

    pub async fn put_wallet_tx_to_wallet_db(
        &self,
        tx_id: &TransactionId,
        wtx: &WalletTx,
    ) -> Result<(), BlockDataStorageError> {
        // let wtx = WalletTx::new(tx);
        let value = bincode::serialize(&wtx).unwrap();
        self.wallet_db
            .put(WalletDbKey::WalletTx(*tx_id), value)
            .await?;
        Ok(())
    }

    // pub async fn update_wallet_tx_in_wallet_db(
    //     &self,
    //     tx_id: &TransactionId,
    //     wtx: &WalletTx,
    // ) -> Result<(), BlockDataStorageError> {
    //     let value = bincode::serialize(wtx).unwrap();
    //     self.wallet_db
    //         .put(WalletDbKey::WalletTx(*tx_id), value)
    //         .await?;

    //     Ok(())
    // }

    // pub async fn del_wallet_tx_from_wallet_db(
    //     &self,
    //     tx_id: &TransactionId,
    // ) -> Result<(), BlockDataStorageError> {
    //     self.wallet_db.erase(WalletDbKey::WalletTx(*tx_id)).await?;
    //     Ok(())
    // }
}

impl WalletStorage {
    #[cfg(debug_assertions)]
    pub async fn get_item_number(&self) -> ItemNumberInWalletDb {
        self.wallet_db.get_item_number().await
    }
}
